﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace MiData
{
    /// <summary>
    /// 模拟表映射 所有表需要主键, 在保存更新，删除时使用
    /// </summary>
    public partial class MiActiveRecord : PropertyChangedBase
    {
        public abstract string TableName { get; }
        public virtual string PkField { get { return "ID"; } }

        /// <summary>
        /// 主键, 所有使用的，必须添加ID字段，可通过其它方式修改
        /// </summary>
        [Column(Name = "ID", Automatic = true, DbType = "int(10)", AutoSync = AutoSync.Never)]
        public  int ID { get; set; }

        public bool IsNew { get; set; }

        internal string BaseSelect = "SELECT * FROM {0}";
        internal string BaseDelete = "DELETE FROM {0} WHERE {1}";

        public MiActiveRecord()
        {
            IsNew = true;
        }

        /// <summary>
        /// 删除一个数据
        /// </summary>
        /// <param name="field"></param>
        /// <param name="v"></param>
        /// <returns></returns>
        public virtual bool Delete(string field, object v)
        {
            bool r = false;
            string sql = String.Format(BaseDelete, TableName, field + "= @value" );

            DbCommand cmd = this.CreateCommand(sql);
            BuildParameter(cmd, new MiParameter("@value",v));
            int count = cmd.ExecuteNonQuery();
            r = (count != 0);
            return r;
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <returns></returns>
        public virtual int Update()
        {
            int r = 0;
            string sql = "UPDATE {0} SET {1} WHERE {2}";
            PropertyInfo[] ps = this.GetType().GetProperties();

            string fieldVs = "", where="1 = 1";
            List<MiParameter> miparams = new List<MiParameter>();

            foreach (PropertyInfo info in ps)
            {
                object[] attributes = info.GetCustomAttributes(typeof(Column), false);
                if (attributes.Length > 0)
                {
                    Column cn = attributes[0] as Column;
                    object va = info.GetValue(this, null);
                    
                    string field = cn.Name;
                    string uf = field + "=@" + field;
                    
                    if (cn.Name == PkField)
                    {
                        where += " AND " + uf;
                    }
                    else
                    {

                        if (fieldVs != "") { fieldVs += ","; }

                        fieldVs += uf;
                    }

                    miparams.Add(new MiParameter("@" + field, info.GetValue(this, null)));
                }
            }
            sql = string.Format(sql, TableName,  fieldVs, where);
            DbCommand cmd = this.CreateCommand(sql);
            this.BuildParameter(cmd, miparams.ToArray());

            r = cmd.ExecuteNonQuery();
            cmd.Dispose();
            return r;
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <returns></returns>
        public virtual long Save()
        {
            if (!this.IsNew)
            {
                return this.Update();
            }
            long r = 0;
            string sql = "INSERT INTO {0} ({1}) VALUES ({2})";

            PropertyInfo[] ps = this.GetType().GetProperties();

            string fields = "", fieldVs = "";
            List<MiParameter> miparams = new List<MiParameter>();

            foreach (PropertyInfo info in ps)
            {
                object[] attributes = info.GetCustomAttributes(typeof(Column), false);
                if (attributes.Length > 0)
                {
                    Column cn = attributes[0] as Column;
                    object va = info.GetValue(this, null);

                    if (cn.Automatic && (va == null || va.ToString() == "" || va.ToString() == "0")) continue;

                    string field = cn.Name;

                    if (fields != "") { fields += ","; fieldVs += ","; }
                    fields += field;
                    fieldVs += "@" + field;

                    var value = info.GetValue(this, null);
                    if (info.PropertyType == typeof(DateTime))
                    {
                        value = ((DateTime)value).ToShortDateString();
                    }

                    miparams.Add(new MiParameter("@" + field, value));
                }
            }

            sql = string.Format(sql, TableName, fields, fieldVs);
            DbCommand cmd = this.CreateCommand(sql);
            this.BuildParameter(cmd, miparams.ToArray());

            cmd.ExecuteNonQuery();
            r = this.GetLastInsertID();
            return r;
        }

        public virtual int Count(string query)
        {
            string sql = "SELECT COUNT(*) FROM {0} WHERE {1}";
            sql = string.Format(sql, TableName, query);
            DbCommand cmd = this.CreateCommand(sql);
            return int.Parse(cmd.ExecuteScalar().ToString());
        }

        public virtual int Count(string field, object value)
        {
            string sql = "SELECT COUNT(*) FROM {0} WHERE {1}=@value";
            sql = string.Format(sql, TableName, field);
            DbCommand cmd = this.CreateCommand(sql, new MiParameter("@value", value));
            return int.Parse(cmd.ExecuteScalar().ToString());

        }

        /// <summary>
        /// 查询一个
        /// </summary>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual object Find(string field, object value)
        {
            IList il = FindAll(field, value, 1);
            if (il.Count > 0) return il[0];
            else return null;
        }

        /// <summary>
        /// 直接通过传递 sql查询 返回数据
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public virtual List<T> FindAll2<T>(string query)
        {
            List<T> o = new List<T>();
            string sql = "SELECT * FROM {0} WHERE 1=1 AND {1}";
            sql = String.Format(sql, this.TableName, query);

            DbCommand cmd = this.CreateCommand(sql);

            DbDataReader reader = cmd.ExecuteReader();

            PropertyInfo[] ps = this.GetType().GetProperties();

            while (reader.Read())
            {
                o.Add((T)_referObject(reader, ps));
            }
            reader.Close();
            cmd.Dispose();

            return o;

        }

        /// <summary>
        /// 返回查询的所有数据。 where field = value
        /// </summary>
        /// <param name="filed"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual IList FindAll(string field, object value, int limit = -1)
        {
            IList o = new List<object>();
            string sql = "SELECT * FROM {0} WHERE 1=1 AND {1}=@value";
            sql = String.Format(sql, this.TableName, field);
            if (limit != -1)
            {
                sql += " LIMIT " + limit;
            }

            DbCommand cmd = this.CreateCommand(sql, new MiParameter("@value", value));

            DbDataReader reader = cmd.ExecuteReader();

            PropertyInfo[] ps = this.GetType().GetProperties();

            while (reader.Read())
            {
                o.Add(_referObject(reader, ps));
            }
            reader.Close();
            cmd.Dispose();

            return o;
        }

        /// <summary>
        /// 所有数据 较慢。可以在子类的重写
        /// </summary>
        /// <returns></returns>
        public virtual IList AllData()
        {
            return FindAll("1", 1);
        }

        /// <summary>
        /// 通过反射机制，将一条数据库记录，映射为一个实体对象
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="ps"></param>
        /// <returns></returns>
        private object _referObject(DbDataReader reader, PropertyInfo[] ps)
        {
            Type t = this.GetType();    //创建子类对象
            var b = Activator.CreateInstance(t);
            foreach (PropertyInfo info in ps)
            {
                object[] attributes = info.GetCustomAttributes(typeof(Column), false);
                if (attributes.Length > 0)
                {
                    string field = ((Column)attributes[0]).Name;
                    try
                    {
                        string dbValue = reader[field].ToString();
                        object entiryValue = null;

                        if (info.PropertyType == typeof(string))
                        {
                            entiryValue = dbValue;
                        }
                        else if (info.PropertyType == typeof(int))
                        {
                            if (dbValue.ToString() == "") entiryValue = 0;
                            else  entiryValue = int.Parse(dbValue.ToString());
                        }
                        else if (info.PropertyType == typeof(long))
                        {
                            entiryValue = long.Parse(dbValue.ToString());
                        }
                        else if (info.PropertyType == typeof(TimeSpan))
                        {
                            entiryValue = TimeSpan.FromTicks(long.Parse(dbValue));
                        }
                        else if (info.PropertyType == typeof(DateTime))
                        {
                            if (dbValue == "") continue;
                            entiryValue = Convert.ToDateTime(dbValue);
                        }
                        else if (info.PropertyType.BaseType == typeof(Enum))
                        {
                            if (dbValue != "")
                            {
                                entiryValue = Enum.Parse(info.PropertyType, dbValue);
                            }
                        }
                        if (entiryValue != null)
                        {
                            info.SetValue(b, entiryValue, null);
                        }
                    }
                    catch (System.IndexOutOfRangeException)
                    {

                    }
                }
            }
            (b as MiActiveRecord).IsNew = false;
            return b;
        }
    }
}
